﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

namespace StateMachine
{
	public class Tape : INotifyPropertyChanged
	{
		public const string DELTA = "Δ";
        //Getting a dictionary to implement INotifyCollection Changed... Not here
        private Dictionary<int, char?> _tapeDict;
        public Dictionary<int, char?> TapeDict { get { return _tapeDict; } set { _tapeDict = value; NotifyPropertyChanged("TapeDict"); } }

        public Tape()
        {
            TapeDict = new Dictionary<int, char?>();
            Head = 0;
        }

		public char? Current
		{
			get { return TapeDict.ContainsKey(Head) ? TapeDict[Head] : null; }
            private set { if (value != null) TapeDict[Head] = value; else TapeDict.Remove(Head); NotifyPropertyChanged("Current"); }
		}

		public void ApplyTransition(Transition transition)
		{
			if (Current != transition.Read)
			{
				throw new InvalidTransitionException("Transition does not accept current tape value");
			}
			Current = transition.Write;
			Head += (int)transition.Direction;
		}

        private int _head;
        public int Head { get { return _head; } private set { _head = value; NotifyPropertyChanged("Head"); } }

		public void Clear()
		{
			TapeDict.Clear();
			Head = 0;
		}

        public void ResetHead()
        {
            Head = 0;
        }

        public void LoadString(string tape)
        {
            this.Clear();
            foreach (char c in tape)
            {
                Current = c;
                Head++;
            }
            Head = 0;
        }

        public override string ToString()
        {
            if (TapeDict.Count < 1)
                return DELTA;
            int min = TapeDict.Keys.Min() - 1;
            int max = TapeDict.Keys.Max() + 2;

            StringBuilder result = new StringBuilder(max - min);

            for (int i = min; i < max; i++)
            {
                result.Append(TapeDict.ContainsKey(i) ? TapeDict[i] : DELTA[0]);
            }

            return result.ToString();
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string name)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(name));
        }

        #endregion
    }

	public enum TapeMovement
	{
		L = -1, S = 0, R = 1
	}

	public class InvalidTransitionException : Exception
	{
		public InvalidTransitionException()
		{
		}

		public InvalidTransitionException(String message) : base(message)
		{
		}
	}

}
